package tmt.usercenter.web.controller;

import com.tmt.helper.RandomCodeHelper;
import com.tmt.sm.impl.ReturnMode;
import com.tmt.spring.validation.RegisterValidateHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.WebAsyncTask;
import tmt.usercenter.web.controller.bean.WorkFlowManager;
import tmt.usercenter.web.controller.bean.WorkFlowNode;
import tmt.usercenter.web.controller.validtor.MobileValidateInfoValidator;
import tmt.usercenter.web.domain.ClientSystem;
import tmt.usercenter.web.domain.TmtUser;
import tmt.usercenter.web.domain.enums.FuncPointType;
import tmt.usercenter.web.domain.security.UserDetailsImpl;
import tmt.usercenter.web.service.ClientSystemService;
import tmt.usercenter.web.service.SmHistoryService;
import tmt.usercenter.web.service.TmtUserService;
import tmt.usercenter.web.service.UserInfoConfigService;
import tmt.usercenter.web.vo.HttpResult;
import tmt.usercenter.web.vo.user.MobileValidateInfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 验证手机号码的流程
 */

@Controller
@RequestMapping("/mobile")
public class MobileController extends BaseController {

    private static final String MOBILE_VALIDATE_INFO = "_MOBILE_VALIDATE_INFO_";
    private static final String MOBILE_VALIDATE_FLOW = "redirect:/mobile/flow";
    private static final String MOBILE_VALIDATE_PAGE = "redirect:/mobile/validate";

    private static final List<String> MOBILE_FLOW_NODES = new ArrayList<String>() {{
        add(MOBILE_VALIDATE_FLOW);
        add(MOBILE_VALIDATE_PAGE);
    }};

    @Autowired
    private MobileValidateInfoValidator mviValidator;

    @Autowired
    private TmtUserService userService;

    @Autowired
    private SmHistoryService smHistoryService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private ClientSystemService clientSystemService;

    @Autowired
    private UserInfoConfigService userInfoConfigService;

    @InitBinder("validateInfo")
    @Override
    protected void initBinder(WebDataBinder binder) {
        super.initBinder(binder);
        binder.addValidators(mviValidator);
    }

    @RequestMapping(value = "/flow", method = RequestMethod.GET)
    public String validateFlow(HttpServletRequest request, Model model) {

        //如果当前请求不处于当前流程节点上，则直接跳转到当前流程节点对应的请求处。
        String url = WorkFlowManager.assure(request, null);
        if (!StringUtils.isEmpty(url))
            return url;

        //获取当前流程节点。
        WorkFlowNode flowNode = WorkFlowManager.current(request);
        if (flowNode == null  //如果没有绑定工作流，则立即绑定工作流。
                || !WorkFlowManager.containNodes(flowNode, MOBILE_FLOW_NODES)) {
            WorkFlowManager.insertFlowNodes(request, MOBILE_FLOW_NODES);
        }

        return WorkFlowManager.next(request, "redirect:/mobile/validate");
    }

    @RequestMapping(value = "/validate", method = RequestMethod.GET)
    public String validateMobile(HttpServletRequest request, Model model) {
        //如果当前请求不处于当前流程节点上，则直接跳转到当前流程节点对应的请求处。
        String url = WorkFlowManager.assure(request, null);
        if (!StringUtils.isEmpty(url))
            return url;

        //获取当前需要验证的手机号码。
        MobileValidateInfo validateInfo = null;
        Object obj = request.getSession().getAttribute(MOBILE_VALIDATE_INFO);
        if (obj == null) {
            Long userId = null;
            String mobile = "";
            String clientId = "";
            boolean readOnly = true;
            Object user = request.getSession().getAttribute(RegisterController.NEW_USER);
            if (user != null) {
                userId = ((TmtUser) user).getId();
                mobile = ((TmtUser) user).getMobile();
                clientId = ((TmtUser) user).getOriginSystem().getClientId();
                readOnly = ((TmtUser) user).isMobileEnabled();
            } else {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication != null
                        && authentication.getPrincipal() != null
                        && authentication.getPrincipal() instanceof UserDetailsImpl) {
                    userId = ((UserDetailsImpl) authentication.getPrincipal()).getUserId();
                    mobile = ((UserDetailsImpl) authentication.getPrincipal()).getMobile();
                    clientId = ((UserDetailsImpl) authentication.getPrincipal()).getClientId();
                    readOnly = ((UserDetailsImpl) authentication.getPrincipal()).isMobileEnabled();
                }
            }
            validateInfo = new MobileValidateInfo();
            validateInfo.setUserId(userId);
            validateInfo.setMobile(mobile);
            validateInfo.setClientId(clientId);
            validateInfo.setMobileReadOnly(readOnly);
            request.getSession().setAttribute(MOBILE_VALIDATE_INFO, validateInfo);
        } else {
            validateInfo = (MobileValidateInfo) obj;
        }

        model.addAttribute("validateInfo", validateInfo);
        return "user/mobile_validate";
    }

    @RequestMapping(value = "/validate", method = RequestMethod.POST)
    public String validateMobile(HttpServletRequest request,
                                 HttpServletResponse response,
                                 @Valid @ModelAttribute("validateInfo") MobileValidateInfo mobileValidateInfo,
                                 BindingResult result, Model model) {
        if (result.hasErrors()) {
            model.addAttribute("validateInfo", mobileValidateInfo);
            return "user/mobile_validate";
        }

        //必须取Session中保存的手机号码作为真实的手机号码。
        MobileValidateInfo validateInfo = (MobileValidateInfo) request
                .getSession().getAttribute(MOBILE_VALIDATE_INFO);

        //更新用户对象的mobileEnabled属性。
        Object obj = request.getSession().getAttribute(RegisterController.NEW_USER);
        if (obj != null) {
            TmtUser user = (TmtUser) obj;
            user.setMobile(validateInfo.getMobile());
            user.setMobileEnabled(true);
        } else {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null
                    && authentication.getPrincipal() != null
                    && authentication.getPrincipal() instanceof UserDetailsImpl) {
                Long userId = ((UserDetailsImpl) authentication.getPrincipal()).getUserId();
                boolean value = !((UserDetailsImpl) authentication.getPrincipal()).isMobileEnabled();
                userService.updateMobileEnabled(userId, validateInfo.getMobile(), value);
                ((UserDetailsImpl) authentication.getPrincipal()).setMobileEnabled(value);
            }
        }

        request.getSession().removeAttribute(MOBILE_VALIDATE_INFO);

        //进入下一个流程
        return WorkFlowManager.next(request, "user/mobile_validate_success");
    }

    @ResponseBody
    @RequestMapping(value = "/code/{mobile}", method = RequestMethod.POST)
    public WebAsyncTask<HttpResult> sendSms(HttpServletRequest request,
                                            @PathVariable("mobile") String mobile) {
        //读取Session中保存的MobileValidateInfo对象
        Object obj = request.getSession().getAttribute(MOBILE_VALIDATE_INFO);
        if (obj == null) {
            return new WebAsyncTask<>(() -> HttpResult.failure("访问无效"));
        }

        MobileValidateInfo validateInfo = (MobileValidateInfo) obj;

        ////尝试效验手机号码
        if (!mobile.equals(validateInfo.getMobile())) {
            //将正真接收短信的手机号码保存下来。
            validateInfo.setMobile(mobile);
            if (!RegisterValidateHelper.validateMobile(mobile)) {
                return new WebAsyncTask<>(() -> HttpResult.failure("手机号码无效"));
            }
        }

        try {
            userInfoConfigService.validateMobileRule(validateInfo.getMobile(), validateInfo.getUserId(), validateInfo.getClientId());
        } catch (Exception ex) {
            return new WebAsyncTask<>(() -> HttpResult.failure(ex.getMessage()));
        }

        ClientSystem clientSystem = clientSystemService.findByClientId(validateInfo.getClientId());

        Callable<HttpResult> callable = () -> {

            String code = RandomCodeHelper.getNumberCode(6);
            String content = MessageFormat.format("{0}注册验证码：{1}", clientSystem.getName(), code);

            HttpResult result = smHistoryService.sendSm(validateInfo.getClientId(),
                    mobile, content, FuncPointType.USER_REGISTER, false, ReturnMode.SYNC_MODE);

            if (result.isSuccess()) {
                validateInfo.setMobile(mobile);
                validateInfo.setSendCode(passwordEncoder.encode(code));
            }

            return result.isSuccess() ? HttpResult.success(result.getMessage(), validateInfo)
                    : HttpResult.failure(result.getMessage(), validateInfo);
        };

        return new WebAsyncTask<>(callable);
    }

    /**
     * 预先初始化Session中的MobileValidateInfo对象。
     *
     * @param request
     * @param validateInfo
     */
    public static void initialMobileValidateInfo(HttpServletRequest request,
                                                 MobileValidateInfo validateInfo) {
        request.getSession().setAttribute(MOBILE_VALIDATE_INFO, validateInfo);
    }

}
